/**
 * This is the main analyzer program file.
 */
#include <string.h>
#include "simpletext.h"
#include "propeller.h"

#include "analyzer.h"

PAR_t gPar;

static int gWaitOk = 1;
static int gQueue[MAX_SAMPLES+1];

/*
 * wait for last command to finish or gWaitOk off
 */
static inline void waitCmd()
{
  while(gPar.mailbox.cmd && gWaitOk)
    ;
}

/*
 * send a command
 */
static inline void sendCmd(int cmd)
{
  waitCmd();
  /* let dump know how to interpret data */
  gPar.mailbox.last = cmd;
  gPar.mailbox.wrap = 0;
  gPar.mailbox.pos  = gQueue;
  /* send command */
  gPar.mailbox.cmd  = cmd;      
}

/*
 * Dump data as streams.
 *
 * Take data like this:
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 * 0xf0000000 mask 0xfc00000
 *
 * And make it look like this for the user:
 * 11111111
 * 11111111
 * 11111111
 * 11111111
 * 00000000
 * 00000000
 */
static int dump(int* ptr, int len)
{
  /* !!!!!!!!!!!!!!! Danger! Functions must set ptr and len properly !!!!!!!!!!!!!!! */
  int j = 0;
  int n = 0;
  putChar('\n');
  for(j = 31; len && j > -1; j--) {
    if((1<<j) & gPar.mailbox.pins) {
      putDec(j);
      putChar(' ');
      for(n = 0; n < len; n++) {
        putBin(((1<<j) & ptr[n]) ? 1 : 0);
      }
      putChar('\n');
    }
  }
  return len;
}

static int dumpGoat()
{
  int len = (gPar.mailbox.pos - gQueue);
  dump(gQueue, len);
  return len;
}

static int dumpGoto()
{
  int len = 0;
  int n;
  if(gPar.mailbox.wrap == 0) {
    /* begin to pos */
    n = (int)gPar.mailbox.pos - (int)gQueue;
    dump(gQueue, n/4);
    len += n;
  }
  else {
    /* pos to end */
    n = (int)&gQueue[MAX_SAMPLES] - (int)gPar.mailbox.pos;
    dump(gPar.mailbox.pos, n/4);
    len += n;
    /* begin to pos */
    n = (int) gPar.mailbox.pos - (int) gQueue;
    dump(gQueue, n);
    len += n;
  }

  return len;
}

static int dumpData()
{
  if(gPar.mailbox.last == CMD_GOAT)
    return dumpGoat();
  else if(gPar.mailbox.last == CMD_GOTO)
    return dumpGoto();
  return 0;
}

static inline int dumpData2()
{
  int n;
  int len = ((int)gPar.mailbox.pos - (int)gQueue)/4;
  text_t *term = simpleterm_pointer();
  /* if head <= tail, no data */
  for(n = 0; n < len; n++) {
    writeHexDigits(term,gQueue[n], 8);
    putLine("");
  }
  return len;
}

static inline void dumpInfo()
{
  putStr("INA $");
  putHex(INA);
  putStr(" Pins $");
  putHex(gPar.mailbox.pins);
  putStr(" Arm $");
  putHex(gPar.mailbox.arm);
  putStr(" Trig $");
  putHex(gPar.mailbox.trig);
  putStr(" Size ");
  putDec(gPar.mailbox.size);
  putStr(" Queue $");
  putHex((int)gQueue);
  putStr(" Pos $");
  putHex((int)gPar.mailbox.pos);
  putStr(" Wrap ");
  putDec(gPar.mailbox.wrap);
  putStr(" Last ");
  putDec(gPar.mailbox.last);
  putStr(" Rate ");
  putDec(gPar.mailbox.rate);
  putStr(" Delay ");
  putDec(gPar.mailbox.delay);
  putLine("");
}

/*
 Start a new COG running the analyzer.cogc code
 */
static int start(void)
{
  int cog;
#if defined(__PROPELLER_USE_XMM__)
  int size = _load_stop_analyzer_cog - _load_start_analyzer_cog;
  unsigned int cogbuffer[size];
  /* memcpy does bytes, so we copy numbytes * 4 = num ints. */      
  memcpy(cogbuffer, _load_start_analyzer_cog, size<<2);
#else
  int *cogbuffer = (int*)_load_start_analyzer_cog;
#endif
  gPar.mailbox.cmd  = CMD_LAST;
  gPar.mailbox.qptr = &gQueue[0];
  cog = cognew(cogbuffer, &gPar.mailbox);
  gWaitOk = 1;
  waitCmd(); /* wait for cog to start up */
  return cog;
}

/*
  Stop the running cog
 */
static int stop(int cog)
{
  if (cog > -1) {
      gWaitOk = 0;
      cogstop(cog);
      cog = -1;
  }
  return cog;
}

static void usage()
{
  putLine("'dump' Dump data if available.");
  /* putLine("'exit' Exit program."); */
  putLine("'goat' Start sample on trigger.");
  putLine("'goto' Collect sample until trigger.");
  putLine("'ina'  Show binary INA value.");
  putLine("'inax' Show hexadecimal INA value.");
  putLine("'info' Show settings information.");
  putLine("'reset' Stop and restart sampling cog.");
  putLine("'arm  <long>' Set a hex word to find before a trigger can start.");
  putLine("'pins <long>' Set a hex pin mask word. bits not set are ignored.");
  putLine("'rate <long>' Set a hex sample rate. Delay = CLKFREQ/rate.");
  putLine("'size <long>' Set a hex number of samples to collect.");
  putLine("'trig <long>' Set the hex trigger word to find before sample.");
}

/*
  Make a serial command loop according to usage.
 */
int main(void)
{
  char buf[41];
  char cmd[10];
  int  data;
  int  cog = start();

  gPar.mailbox.arm  = INA;
  gPar.mailbox.trig = gPar.mailbox.arm;
  gPar.mailbox.pins = 0xff000000;
  gPar.mailbox.size = 128;
  gPar.mailbox.pos  = 0;
  gPar.mailbox.wrap = 0;
  gPar.mailbox.rate = MAX_RATE;
  gPar.mailbox.delay = CLKFREQ/MAX_RATE;

  for (;;) {

    //putLine("");
    putLine("CMD>");
    getStr(buf, 40);

    if(!strcmp("dump", buf)) {
      dumpData();
    }
    else if(!strcmp("goat", buf)) {
      /* goat command can take forever by design.
         Restart before command ensures that something happens.
       */
      stop(cog);
      cog = start();
      sendCmd(CMD_GOAT);
    }
    else if(!strcmp("goto", buf)) {
      /* goto command can take forever by design.
         Restart before command ensures that something happens.
       */
      stop(cog);
      cog = start();
      sendCmd(CMD_GOTO);
    }
    else if(!strcmp("ina", buf)) {
      writeBinDigits(simpleterm_pointer(),INA,32);
      putLine("");
    }
    else if(!strcmp("inax", buf)) {
      writeHexDigits(simpleterm_pointer(),INA,8);
      putLine("");
    }
    else if(!strcmp("info", buf)) {
      dumpInfo();
    }
    else if(!strcmp("reset", buf)) {
      stop(cog);
      cog = start();
    }
    else if(*buf == '?') {
      usage();
    }
#if 0
    else if(!strcmp("exit", buf)) {
      putLine("CMD>");
      putChar(0xff);
      putChar(0x00);
      putChar(0x00);
      return 0;
    }
#endif
#if 0
    else if(!strncmp("rate", buf, 4)) {
      sscan(buf,"%s %d", cmd, &data);
      if (data > MAX_RATE) {
        data = MAX_RATE;
      }
      else if (data < MIN_RATE) {
        data = MIN_RATE;
      }
      gPar.mailbox.rate  = data;
      gPar.mailbox.delay = CLKFREQ/data;
      putStr("Delay ");
      putDec(gPar.mailbox.delay);
      putLine("");
    }
#endif
    else {
      sscan(buf,"%s %x", cmd, &data);
      if(!strcmp("arm", cmd)) {
        gPar.mailbox.arm = data;
      }
      else if(!strcmp("size", cmd)) {
        gPar.mailbox.size = data;
      }
      else if(!strcmp("pins", cmd)) {
        gPar.mailbox.pins = data;
      }
      else if(!strcmp("trig", cmd)) {
        gPar.mailbox.trig = data;
      }
#if 1
      else if(!strcmp("rate", cmd)) {
        if (data > MAX_RATE) {
          data = MAX_RATE;
        }
        else if (data < MIN_RATE) {
          data = MIN_RATE;
        }
        gPar.mailbox.rate  = data;
        gPar.mailbox.delay = CLKFREQ/data;
        putStr("Delay ");
        putDec(gPar.mailbox.delay);
        putLine("");
      }
#endif
    }
  }
  return 0;
}

